#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

#include "pub_log.h"
#include "thread_pool.h"
#include "ipcsvr.h"

#define SOCKET int
#define MAX_LINK_NUM_INT 10
#define PORT (19527)
#define MAX_EVENTS (2048)
#define MAXLEN (2048)
#define SWEXIT (1)
struct stCommCmd
{
    int type;
    char buffer[128];       /*信息*/
};
struct thread_data
{
    int fd;
    char data[MAXLEN];
    int len;
};


void *do_use_fd(void *connfd);

void exit_main()
{
    slog_debug("exiting--------------");
    WrLog_release();
    exit(1);
}
void sigint_handle(int sig)
{
    exit_main();
}
void signal_ignore()
{
    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGHUP, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    signal(SIGALRM, SIG_IGN);
    signal(SIGABRT, SIG_IGN);
    signal(SIGBUS, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

}
int main(int argc, char **argv)
{
    int ret;

    //signal(SIGINT, sigint_handle);
    signal_ignore();

    slog_info("init log!");
    ret = WrLog_init_by_logname_logpath("tcp_server", "./");
    if(ret < 0)
    {
        fprintf(stderr, "WrLog_init err!");
        return -1;
    }


    slog_info("start server_main");
    if( ( ret = server_main() ) < 0 )
    {
        slog_err("server_main err! ret = [%d]", ret );
        exit_main();
    }
    else if(ret == 1)
    {
        exit_main();
    }

    return 0;
}

int server_main()
{
    struct epoll_event ev, events[MAX_EVENTS];
    int listen_sock, conn_sock, nfds;
    int ret, n, nread;
    struct thread_data recv;
    struct sockaddr local;
    int addrlen = 1;
    int iUdpFd;
    int epollfd;
    thread_pool_t *pool = NULL;
    struct  stCommCmd stCmd;


    memset( &local, 0x00, sizeof(local) );

    /* Set up listening socket, 'listen_sock' (socket(), bind(), listen()) */
    if( (ret = server_init( &listen_sock, PORT )) < 0)
    {
        slog_err("server_init err! ret = [%d]", ret );
        return -1;
    }
    slog_info("server_init success! listen_sock = [%d]", listen_sock );

    ret = thread_pool_init(&pool, MAX_THREAD);
    if (ret < 0)
    {
        slog_err("Create thread pool failed! ret = [%d]", ret);
        return -1;
    }

    epollfd = epoll_create(65535);//size用来告诉内核这个监听的数目一共有多大 自从Linux 2.6.8开始，size参数被忽略，但是依然要大于0
    if (epollfd == -1)
    {
        slog_err("epoll_create err! (error code: %d - %s)", errno, strerror(errno));
        return -1;
    }
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1)
    {
        slog_err("epoll_ctl err! (error code: %d - %s)", errno, strerror(errno));
        return -1;
    }

    iUdpFd = udp_bind("tcp_lsn");
    if( iUdpFd < 0 )
    {
        slog_err("create socket error! iUdpFd=[%d]", iUdpFd);
        return -1;
    }
    ev.events = EPOLLIN;
    ev.data.fd = iUdpFd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, iUdpFd, &ev) == -1)
    {
        slog_err("epoll_ctl err! (error code: %d - %s)", errno, strerror(errno));
        return -1;
    }
    while(1)
    {

        slog_fflush();
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds == -1)
        {
            slog_err("epoll_wait err! (error code: %d - %s)", errno, strerror(errno));
            exit(EXIT_FAILURE);
        }

        for (n = 0; n < nfds; ++n)
        {
            /*消息来自管理层*/
            if (events[n].data.fd == iUdpFd)
            {
                slog_info("Data is available now.");
                memset(&stCmd, 0x00, sizeof(struct stCommCmd));
                ret = udp_recv(iUdpFd, (char *)&stCmd, sizeof(struct stCommCmd));
                if( ret < 0 )
                {
                    slog_err("udp_recv err! errno=[%d][%s]", errno, strerror(errno));
                    continue;
                }
                slog_info("udp_recv data.[%d %s]", stCmd.type, stCmd.buffer);
                if(stCmd.type == SWEXIT)
                {
                    if(pool == NULL)
                    {
                        thread_pool_destroy(pool);
                        pool = NULL;
                    }
                    return 1;
                }
                udp_send(iUdpFd, (char *)&stCmd, sizeof(struct stCommCmd), "swman");
            }
            /*外部连接*/
            else if (events[n].data.fd == listen_sock)
            {
                conn_sock = accept(listen_sock, (struct sockaddr *) &local, &addrlen);
                if (conn_sock == -1)
                {
                    slog_err("accept err! (error code: %d - %s)", errno, strerror(errno));
                }
                slog_info("new connect! sockfd = [%d]", conn_sock);
                setnonblocking(conn_sock);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = conn_sock;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1)
                {
                    slog_err("epoll_ctl err! (error code: %d - %s)", errno, strerror(errno));
                }
            }
            /*外部连接数据*/
            else
            {
                memset(&recv, 0x00, sizeof(recv));
                nread = read(events[n].data.fd, recv.data, MAXLEN);//读取客户端socket流
                if (nread == 0)
                {
                    slog_err("client close the connection sockfd = [%d]", events[n].data.fd);
                    close(events[n].data.fd);
                    continue;
                }
                if (nread < 0)
                {
                    slog_err("read err! sockfd = [%d] (error code:%d - %s)", events[n].data.fd, errno, strerror(errno) );
                    close(events[n].data.fd);
                    continue;
                }
                recv.fd = events[n].data.fd;
                recv.len = strlen(recv.data);
                thread_pool_add_worker(pool, do_use_fd, (void *)(&(recv)));
            }
        }
    }
}
int server_init( SOCKET *server_sockfd, const int port_int )
{
    struct sockaddr_in server_addr;
    int val = 1;

    // socket
    if( (*server_sockfd = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
    {
        slog_err( "Error: create socket! (error code: %d - %s)", errno, strerror(errno) );
        return -1;
    }

    // addr
    setsockopt(*server_sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof (val));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons( port_int );
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero( &(server_addr.sin_zero), 8 );

    // bind
    if( bind( *server_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1 )
    {
        slog_err( "Error: bind! (error code: %d - %s)", errno, strerror(errno) );
        return -1;
    }

    // listen
    if( listen( *server_sockfd, MAX_LINK_NUM_INT ) == -1 )
    {
        slog_err("Error: listen! (error code:%d - %s)", errno, strerror(errno) );
        return -1;
    }

    return 0;
}

int setnonblocking(int sockfd)
{
    if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK) == -1)
    {
        return -1;
    }
    return 0;
}
void *do_use_fd(void *data_fd)
{
    int ret;
    struct thread_data recv = *((struct thread_data *)(data_fd));

    slog_err("do_use_data()");

    /*add your deal begin*/
    slog_info("recv data = [%s]", recv.data);
    write(recv.fd, recv.data, recv.len);//响应客户端
    /*add your deal end*/

    ret = close(recv.fd);//任何被侦听的文件描述拊只要其被关闭，那么它也会自动的从被侦听的文件描述符集体中删除。
    if(ret < 0)
    {
        slog_err("Error: close fd = [%d]! (error code:%d - %s)", recv.fd,  errno, strerror(errno) );
    }
    else
    {
        slog_err("close fd = [%d] success!", recv.fd);
    }
    slog_fflush();
    return 0;
}

